package com.gongfutrip.trans.remoting.netty;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

import com.gongfutrip.trans.remoting.InvokeCallback;
import com.gongfutrip.trans.remoting.common.SemaphoreReleaseOnlyOnce;
import com.gongfutrip.trans.remoting.protocol.RemotingCommand;

public class ResponseFuture {
  private final int opaque;
  private final long timeoutMillis;
  private final long beginTimestamp = System.currentTimeMillis();
  private final InvokeCallback invokeCallback;// 客户端发送数据到服务端完成之后回调函数
  private final SemaphoreReleaseOnlyOnce once;
  private volatile Throwable cause;
  private volatile boolean sendRequestOK = true;
  private final CountDownLatch countDownLatch = new CountDownLatch(1);
  private volatile RemotingCommand responseCommand;
  private final AtomicBoolean executeCallbackOnlyOnce = new AtomicBoolean(false);// 保证执行一遍

  public ResponseFuture(int opaque, long timeoutMillis, InvokeCallback invokeCallback,
      SemaphoreReleaseOnlyOnce once) {
    this.opaque = opaque;
    this.timeoutMillis = timeoutMillis;
    this.invokeCallback = invokeCallback;
    this.once = once;
  }

  public void executeInvokeCallback() {
    if (null != invokeCallback) {
      if (this.executeCallbackOnlyOnce.compareAndSet(false, true)) {
        invokeCallback.operationComplete(this);
      }
    }
  }

  public Throwable getCause() {
    return cause;
  }

  public void setCause(Throwable cause) {
    this.cause = cause;
  }

  public boolean isSendRequestOK() {
    return sendRequestOK;
  }

  public void setSendRequestOK(boolean sendRequestOK) {
    this.sendRequestOK = sendRequestOK;
  }

  public RemotingCommand getResponseCommand() {
    return responseCommand;
  }

  public void setResponseCommand(RemotingCommand responseCommand) {
    this.responseCommand = responseCommand;
  }

  public int getOpaque() {
    return opaque;
  }

  public long getTimeoutMillis() {
    return timeoutMillis;
  }

  public long getBeginTimestamp() {
    return beginTimestamp;
  }

  public InvokeCallback getInvokeCallback() {
    return invokeCallback;
  }

  public SemaphoreReleaseOnlyOnce getOnce() {
    return once;
  }

  public void putResponse(final RemotingCommand responseCommand) {
    this.responseCommand = responseCommand;
    this.countDownLatch.countDown();
  }

  @Override
  public String toString() {
    return "ResponseFuture [responseCommand=" + responseCommand + ", sendRequestOK=" + sendRequestOK
        + ", cause=" + cause + ", opaque=" + opaque + ", timeoutMillis=" + timeoutMillis
        + ", invokeCallback=" + invokeCallback + ", beginTimestamp=" + beginTimestamp
        + ", countDownLatch=" + countDownLatch + "]";
  }

  public void release() {
    if (this.once != null) {
      this.once.release();
    }
  }

  public RemotingCommand waitResponse(final long timeoutMillis) throws InterruptedException {
    this.countDownLatch.await(timeoutMillis, TimeUnit.MILLISECONDS);
    return this.responseCommand;
  }
}
